# -*- coding: binary -*-

module Msf
  module Exploit::Remote::SMB::Server
    module Share
      module Command
        module Trans2
          require 'msf/core/exploit/smb/server/share/command/trans2/find_first2'
          require 'msf/core/exploit/smb/server/share/command/trans2/query_file_information'
          require 'msf/core/exploit/smb/server/share/command/trans2/query_path_information'

          # Handles an SMB_COM_TRANSACTION2 command, used to provide support for a richer set of
          # server-side file system handling.
          #
          # @param c [Socket] The client sending the request.
          # @param buff [String] The data including the client request.
          # @return [Integer] The number of bytes returned to the client as response.
          def smb_cmd_trans2(c, buff)
            smb = @state[c]
            pkt = CONST::SMB_TRANS2_PKT.make_struct
            pkt.from_s(buff)

            data_trans2 = CONST::SMB_DATA_TRANS2.make_struct
            data_trans2.from_s(pkt['Payload'].v['SetupData'])

            sub_command = data_trans2.v['SubCommand']
            parameters = data_trans2.v['Parameters'].gsub(/^[\x00]*/, '') #delete padding

            case sub_command
            when CONST::TRANS2_QUERY_FILE_INFO
              return smb_cmd_trans2_query_file_information(c, parameters)
            when CONST::TRANS2_QUERY_PATH_INFO
              return smb_cmd_trans2_query_path_information(c, parameters)
            when CONST::TRANS2_FIND_FIRST2
              return smb_cmd_trans2_find_first2(c, parameters)
            else
              vprint_status("SMB Share - #{smb[:ip]} Unknown SMB_COM_TRANSACTION2 subcommand: #{sub_command.to_s(16)}")
              return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_NT_STATUS_NOT_FOUND, true)
            end
          end

          # Builds and sends an SMB_COM_TRANSACTION2 response.
          #
          # @param c [Socket] The client to answer.
          # @param parameters [Rex::Struct2::CStruct] The SMB_Parameters to include in the response.
          # @param data [Rex::Struct2::CStruct] The SMB_Data to include in the response.
          # @return [Integer] The number of bytes returned to the client as response.
          def send_trans2_res(c, parameters, data)
            pkt = CONST::SMB_TRANS_RES_PKT.make_struct
            smb_set_defaults(c, pkt)

            pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2
            pkt['Payload']['SMB'].v['Flags1'] = FLAGS
            pkt['Payload']['SMB'].v['Flags2'] = FLAGS2
            pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_TRANS2_RES_WORD_COUNT
            pkt['Payload'].v['ParamCountTotal'] = parameters.to_s.length
            pkt['Payload'].v['DataCountTotal'] = data.to_s.length
            pkt['Payload'].v['ParamCount'] = parameters.to_s.length
            pkt['Payload'].v['ParamOffset'] = CONST::SMB_TRANS_RES_PKT_LENGTH
            pkt['Payload'].v['DataCount'] = data.to_s.length
            pkt['Payload'].v['DataOffset'] = CONST::SMB_TRANS_RES_PKT_LENGTH + parameters.to_s.length
            pkt['Payload'].v['Payload'] =
              parameters.to_s +
              data.to_s

            c.put(pkt.to_s)
          end

          # Converts the path to ascii from unicode and normalizes.
          #
          # @param path [String] The path to normalize.
          # @return [String] The normalized path.
          def normalize_path(path)
            normalized = Rex::Text.to_ascii(path).downcase
            normalized.gsub!(/[\x00]*/, '') #delete padding
            normalized.gsub!(/\\x([0-9a-f]{2})/i, '') # delete hex chars

            normalized
          end

          # Expands a path with wildcards, and returns the set of matching files.
          #
          # @param path [String] the path to expand
          # @return [String] The matching file.
          # @todo It's a shortcut atm, make complete wildcard handling.
          # @todo return an Array of matching files.
          def smb_expand(path)
            search_path = path.gsub(/<\./, '*.') # manage wildcards
            extension = File.extname(file_name)
            if search_path =~ /\\\*#{extension}$/
              search_path.gsub!(/\\\*#{extension}$/, "\\#{file_name}")
            end

            search_path
          end
        end
      end
    end
  end
end
